package org.zoomdev.zoom.rpc.client;

import org.zoomdev.zoom.aop.Interceptor;
import org.zoomdev.zoom.aop.Invoker;
import org.zoomdev.zoom.aop.ProxyFactory;
import org.zoomdev.zoom.aop.ProxyObject;
import org.zoomdev.zoom.aop.impl.InterceptorChan;
import org.zoomdev.zoom.aop.impl.ProxyFactoryImpl;
import org.zoomdev.zoom.common.exceptions.ZoomException;
import org.zoomdev.zoom.rpc.MethodId;
import org.zoomdev.zoom.rpc.utils.RpcUtils;
import org.zoomdev.zoom.serializer.Serializer;
import org.zoomdev.zoom.serializer.SerializerFactory;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class ClientContainer {
    private final SerializerFactory serializerFactory;
    private final Serializer commonSerializer;

    final Serializer<List<MethodId>> serializer;


    public ClientContainer(SerializerFactory serializerFactory) {
        this.serializerFactory = serializerFactory;
        this.commonSerializer = serializerFactory.create(null);
        this.serializer = serializerFactory.create(RpcUtils.listType(MethodId.class));
    }

    Handler createHandlerWithConnections(Method method, List<ClientConnection> connections) {
        if (connections.size() == 0) {
            throw new ZoomException("必须提供服务处理器的服务地址");
        }
        if (connections.size() == 1) {
            return createHandler(connections.get(0), method);
        }
        List<Handler> handlers = new ArrayList<>(connections.size());
        for (ClientConnection connection : connections) {
            handlers.add(createHandler(connection, method));
        }
        return new GroupHandler(handlers.toArray(new Handler[handlers.size()]));
    }

    private final ProxyFactory proxyFactory = new ProxyFactoryImpl();

    public Interceptor getInterceptor() {
        return interceptor;
    }

    public void setInterceptor(Interceptor interceptor) {
        this.interceptor = interceptor;
    }


    private Interceptor interceptor;


    class MethodConfig implements Invoker {
        Method method;
        Object instance;
        Handler handler;

        Invoker invoker;

        MethodConfig() {

        }

        void init() {
            this.invoker = this;
            if (interceptor != null) {
                this.invoker = new InterceptorChan(interceptor, this, method);
            }
        }

        public Object invoke(Object[] args) throws Throwable {
            return invoker.invoke(instance, args);
        }

        @Override
        public Object invoke(Object target, Object[] args) throws Throwable {
            return handler.handle(args).get();
        }
    }

    <T> T doRegister(Class<T> type, Map<String, Handler> map) {
        return proxyFactory.createObject(type, new ProxyObject<MethodConfig>() {
            @Override
            public MethodConfig create(Class interfaceOrAbstractClass, Object instance, Method method) {
                MethodConfig config = new MethodConfig();
                config.instance = instance;
                config.handler = map.get(method.toGenericString());
                config.method = method;
                config.init();
                return config;
            }

            @Override
            public Object invoke(MethodConfig config, Object[] args) throws Throwable {
                return config.invoke(args);
            }
        });
    }

    SimpleHandler createHandler(ClientConnection connection, Method method) {
        Serializer argumentSerializer = serializerFactory.createWithTypes(method.getGenericParameterTypes());
        Serializer serializer = serializerFactory.create(method.getGenericReturnType());
        SimpleHandler handler = new SimpleHandler(connection, method, argumentSerializer, serializer, commonSerializer);
        connection.addHandler(handler);
        return handler;
    }

}
